home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The World's Largest Collection of Windows Software
/
The World's Largest Collection of Windows Software - Disc 1.iso
/
connect
/
_j2
/
wsmtpd16
/
debwsmtp.c
< prev
next >
Wrap
C/C++ Source or Header
|
1993-10-09
|
46KB
|
1,681 lines
/* WSMTPSrv.C - Windows SMTP Server
DEBWSMTP.C - Same as above, with copius debugging info.
- WinSock V1.1 required
Author: Ian Blenke
Ian Blenke cannot be held responsible for damages, expressed or implied, for
the use of this software. No commercial use can be made of this product
without the consent of the author. No profit of any kind can be made on the
sale or distribution of this program. If you wish to distribute this program
with other samples of WinSock programming, you must first contact the author
so that he can keep accurate records of its usage. If you write any programs
based on this source code, you may not sell them for any profit without the
written consent of the author. If you incorporate this source code into a
public domain program, all the author requires is a notification that "part
of the code was written by Ian Blenke" and some form of notification that
his name was used in the public domain software distribution. This does not
represent a contract on the part of the author. If any issues cannot clearly
be resolved by reading this text, immediately contact the author.
I don't like such agreements, but in today's world of lawyers and lawbreakers
I have little other choice. Enjoy!
*/
#include "WSMTPSrv.h"
/* DEBUGIT();
Purpose: To print debugging messages to the debugging window/console.
*/
VOID DEBUGIT( LPSTR lpString)
{
static char szErrors[256];
wsprintf((LPSTR)szErrors, (LPSTR)"WSMTPD: %s\n\r", lpString);
OutputDebugString( (LPSTR)szErrors );
}
/* WinMain();
Purpose: The entry point for our app.
*/
UINT PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpszCmdLine, int nCmdShow)
{
MSG msg;
WORD wVersionRequested;
WSADATA wsaData;
DLGPROC dlgProc;
WNDCLASS wndclass;
HICON hIcon;
hInst=hInstance;
lstrcpy((LPSTR)szAppName, (LPSTR)APP_NAME);
DEBUGIT("WinMain() Begin");
if(hPrevInstance)
{
smtpError(IDS_PREV_INSTANCE);
return(NULL);
}
wVersionRequested=WSVERSION;
if(WSAStartup(wVersionRequested, (LPWSADATA)&wsaData))
{
smtpError(IDS_NO_WINSOCK);
return(NULL);
}
if((LOBYTE(wsaData.wVersion)!=1) ||
(HIBYTE(wsaData.wVersion)!=1))
{
smtpError(IDS_BAD_VERSION);
WSACleanup();
DEBUGIT("WinSock version is not v1.1");
return(NULL);
}
#ifdef USE_3D
Ctl3dRegister(hInst);
Ctl3dAutoSubclass(hInst);
#endif /*USE_3D*/
dlgProc=(DLGPROC)MakeProcInstance((FARPROC)DlgProc, hInst);
hWndDlg=CreateDialog(hInst, (LPSTR)dlgWSMTPSRV,
GetDesktopWindow(), dlgProc);
if(!hWndDlg)
{
smtpError(IDS_SMTP_INIT);
#ifdef USE_3D
Ctl3dUnregister(hInst);
#endif // USE_3D
WSACleanup();
DEBUGIT("Modeless dialog window could not be opened");
return(NULL);
}
hIcon=LoadIcon(hInst, MAKEINTRESOURCE(IDI_ICONEMPTY));
if(hIcon != NULL)
{
SetProp(hWndDlg, "icon", hIcon);
}
else DEBUGIT("IDI_ICONEMPTY Resource not found");
SetClassWord(hWndDlg, GCW_HICON, NULL);
ShowWindow(hWndDlg, nCmdShow);
DEBUGIT("Starting smtpInit");
if(smtpInit(INI_FILE))
{
DestroyWindow(hWndDlg);
// smtpError(IDS_SMTP_INIT);
#ifdef USE_3D
Ctl3dUnregister(hInst);
#endif /* USE_3D */
WSACleanup();
DEBUGIT("Daemon initialization error");
return(NULL);
}
while(GetMessage(&msg, NULL, 0, 0))
{
if(!IsDialogMessage(hWndDlg, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
#ifdef USE_3D
Ctl3dUnregister(hInst);
#endif
WSACleanup();
DEBUGIT("WinMain() End");
return msg.wParam;
} /* WinMain */
/* LRESULT CALLBACK DlgProc(HWND, UINT, WPARAM);
Purpose: To handle the main dialog user intervention.
Given/Returns: Standard Dialog procedure
*/
LRESULT CALLBACK DlgProc(HWND hWnd, UINT Msg, WPARAM wParam,
LPARAM lParam)
{
HICON hIcon;
switch(Msg)
{
case WM_INITDIALOG:
{
if(iLogLevel==LOG_LOW)
{
CheckRadioButton(hWnd, IDR_NORMAL, IDR_DEBUG,
IDR_NORMAL);
}
else
{
CheckRadioButton(hWnd, IDR_NORMAL, IDR_DEBUG,
IDR_DEBUG);
}
SetDlgItemText(hWnd, IDE_PATH, (LPSTR)szLocalMailPath);
SetDlgItemText(hWnd, IDE_HOSTNAME, (LPSTR)szLocalHostName);
return(TRUE);
}
break;
case WM_COMMAND:
{
switch(wParam)
{
case IDR_NORMAL:
{
iLogLevel=LOG_LOW;
return(TRUE);
}
case IDR_DEBUG:
{
iLogLevel=LOG_HIGH;
return(TRUE);
}
case IDB_PATH:
{
int iError;
DWORD dwError;
if(iError=GetOpenFileName((OPENFILENAME FAR *)&ofnMailPath))
{
WritePrivateProfileString(SECTION_MAIN, ENTRY_MAILPATH,
(LPSTR)szLocalMailPath,
(LPSTR)INI_FILE);
SetDlgItemText(hWndDlg, IDE_PATH, (LPSTR)szLocalMailPath);
}
else
{ // Debugging
dwError=CommDlgExtendedError();
}
return(TRUE);
}
}
break;
// All of this, for a changing Icon - Sheesh
case WM_PAINT:
{
if(IsIconic(hWndDlg))
{
PAINTSTRUCT ps;
HDC hDC;
DWORD dwOrg;
HICON hIcon = GetProp(hWndDlg, "icon");
if(hIcon != NULL)
{
hDC=BeginPaint(hWndDlg, &ps);
dwOrg = GetWindowOrg(hDC);
SendMessage(hWndDlg, WM_ICONERASEBKGND, hDC, 0L);
DrawIcon(hDC, LOWORD(dwOrg)+2, HIWORD(dwOrg)+2, hIcon);
EndPaint(hWndDlg, &ps);
return(TRUE);
}
}
}
break;
case WM_ERASEBKGND:
{
if(IsIconic(hWndDlg)) return(TRUE);
else return(FALSE);
}
case WM_CLOSE:
{
RemoveProp(hWndDlg, "icon");
netClose();
DestroyWindow(hWndDlg);
return(TRUE);
}
break;
case WM_QUERYDRAGICON:
{
HICON hIcon;
return(hIcon = GetProp(hWndDlg, "icon"));
}
/* Periodically check for mail until user reads it */
case WM_TIMER:
{
if(!smtpMailCheck(szLocalMailPath))
{
KillTimer(hWndDlg, (UINT)wParam);
SetWindowText(hWndDlg, (LPSTR)STRING_NOMAIL);
}
else
{
SetWindowText(hWndDlg, (LPSTR)STRING_HAVEMAIL);
}
return(TRUE);
}
break;
/* Don't let the user ReSize/Maximize the window! */
case WM_INITMENUPOPUP:
{
if(HIWORD(lParam))
{
HMENU hSystemMenu;
hSystemMenu = GetSystemMenu(hWndDlg, FALSE);
EnableMenuItem(hSystemMenu, SC_SIZE, MF_GRAYED);
EnableMenuItem(hSystemMenu, SC_MAXIMIZE, MF_GRAYED);
}
}
break;
case WM_DESTROY:
{
PostQuitMessage(0);
return(TRUE);
}
break;
}
}
return(FALSE);
} /* dlgProc() */
/* void smtpError(int);
Purpose: To print out a resource string with parameters
Given: ResourceID of format string, and variable arguements.
Returns: Nothing.
*/
void smtpError(int ResourceID, ...)
{
char szString[MAXLINE];
char szBuffer[MAXLINE];
va_list vaArgs;
LPSTR lpArg1, lpArg2;
va_start( vaArgs, ResourceID);
lpArg1 = va_arg( vaArgs, LPSTR);
lpArg2 = va_arg( vaArgs, LPSTR);
if(IsIconic(hWndDlg)) // If in server mode, no modals!
{
smtpLog(LOG_HIGH | LOG_TIME, ResourceID, vaArgs);
return;
}
if(!LoadString(hInst, ResourceID, (LPSTR)szString,
MAXLINE))
return;
wsprintf((LPSTR)szBuffer, (LPSTR)szString, lpArg1, lpArg2);
DEBUGIT(szBuffer);
MessageBeep(MB_ICONEXCLAMATION);
MessageBox(NULL, (LPSTR)szBuffer, (LPSTR)szAppName,
MB_OK | MB_ICONEXCLAMATION);
return;
} /* smtpError */
/* void smtpLog(int, ...);
Purpose: To print an item in the "log" listbox.
Given: Resource ID and variable args.
Returns: Nothing
*/
void smtpLog(int iLevel, int ResourceID, ...)
{
char szString[MAXLINE];
char szLine[MAXLINE];
char szTime[30];
LPSTR lpArg1, lpArg2;
va_list vaArgs;
DWORD dwCount;
if((iLevel&(LOG_TIME-1))<iLogLevel) return; // Only log what we want
va_start( vaArgs, ResourceID);
if(!LoadString(hInst, ResourceID, (LPSTR)szLine, MAXLINE))
return;
lpArg1=va_arg(vaArgs, LPSTR);
lpArg2=va_arg(vaArgs, LPSTR);
wsprintf((LPSTR)szString, (LPSTR)szLine, lpArg1, lpArg2);
if(iLevel&LOG_TIME)
{
netGetTimeAndDate((LPSTR)szTime, sizeof(szTime));
wsprintf((LPSTR)szLine, "%s> %s", (LPSTR)szTime, (LPSTR)szString);
}
else
{
lstrcpy((LPSTR)szLine, (LPSTR)szString);
}
DEBUGIT(szLine);
SendDlgItemMessage(hWndDlg, IDL_LOG, LB_ADDSTRING, 0,
(LPARAM)(LPCSTR)szLine);
dwCount=SendDlgItemMessage(hWndDlg, IDL_LOG, LB_GETCOUNT, 0, 0l);
if(dwCount>MAXITEMS)
{
SendDlgItemMessage(hWndDlg, IDL_LOG, LB_RESETCONTENT, 0, 0l);
wsprintf((LPSTR)szLine, "%s> Reset Log - over %d lines", (LPSTR)szTime,
MAXITEMS);
DEBUGIT(szLine);
SendDlgItemMessage(hWndDlg, IDL_LOG, LB_ADDSTRING, 0,
(LPARAM)(LPCSTR)szLine);
}
} /* smtpLog */
/* BOOL smtpInit(PSTR);
Purpose: To setup the SMTP protocol and structures.
Given: A filename.
Returns: TRUE on error, FALSE if not.
*/
BOOL smtpInit(PSTR pFilename)
{
LPSTR lpString;
HANDLE hMenu;
ofnMailPath.lStructSize=sizeof(ofnMailPath);
ofnMailPath.hwndOwner=hWndDlg;
ofnMailPath.hInstance=hInst;
szMailPathFilter[0]='\0';
LoadString(hInst, IDS_FILTER,
(LPSTR)szMailPathFilter, sizeof(szMailPathFilter));
ofnMailPath.lpstrFilter=(LPSTR)szMailPathFilter;
ofnMailPath.nFilterIndex=2;
ofnMailPath.lpstrCustomFilter=(LPSTR)NULL;
ofnMailPath.lpstrFile=(LPSTR)szLocalMailPath;
ofnMailPath.nMaxFile=sizeof(szLocalMailPath);
ofnMailPath.lpstrFileTitle=(LPSTR)NULL;
ofnMailPath.lpstrInitialDir=(LPSTR)NULL;
ofnMailPath.lpstrTitle=(LPSTR)szAppName;
ofnMailPath.Flags=OFN_CREATEPROMPT | OFN_NOREADONLYRETURN;
ofnMailPath.lpstrDefExt=(LPSTR)NULL;
// Initialize network windows & such
if(netInit()) return(TRUE);
iLogLevel=GetPrivateProfileInt(SECTION_MAIN, ENTRY_DEBUG,
LOG_HIGH, (LPSTR)pFilename);
GetPrivateProfileString(SECTION_MAIN, ENTRY_MAILPATH,
(LPSTR)DEFAULTMAILFILE,
(LPSTR)szLocalMailPath,
sizeof(szLocalMailPath),
(LPSTR)pFilename);
if(iLogLevel==LOG_LOW)
{
CheckRadioButton(hWndDlg, IDR_NORMAL, IDR_DEBUG,
IDR_NORMAL);
}
else
{
CheckRadioButton(hWndDlg, IDR_NORMAL, IDR_DEBUG,
IDR_DEBUG);
}
SetDlgItemText(hWndDlg, IDE_PATH, (LPSTR)szLocalMailPath);
SetDlgItemText(hWndDlg, IDE_HOSTNAME, (LPSTR)szLocalHostName);
return(FALSE);
} /* smtpInit */
/* BOOL smtpServer(LPSMTPCLIENT);
Purpose: To be the heart & soul of the SMTP
server.
Given: pointer to the Client that was active.
Returns: FALSE
Notes: This is the heart & soul of the beast.
*/
BOOL smtpServer(LPSMTPCLIENT lpClient)
{
static char szLine[MAXSNDBUFF]; // These are still holdovers
static char szPaddedLine[MAXSNDBUFF]; // They should be replaced
PSTR pLine;
int iDebug;
BOOL bCmdOk;
if(!lpClient) return(TRUE);
DEBUGIT("smtpServer() Begin");
// 8BITMIME hack
if((lpClient->iExtendedFlags & ESMTP_8BITMIME) &&
(lpClient->iState == STATE_WAIT_FOR_DOT))
{
DEBUGIT("ESMTP_8BITMIME && STATE_WAIT_FOR_DOT");
while(clientReceiveBlock(lpClient, szLine, sizeof(szLine)))
{
if(lpClient->hfFile!=HFILE_ERROR)
{
iDebug=_lwrite(lpClient->hfFile,
(LPSTR)szLine, lstrlen((LPSTR)szLine));
}
} // While there is data
} // If in DATA state with 8BITMIME active.
// This is a hack for when a client QUIT's.
bConnectionQuit=FALSE;
// Get as many lines as are waiting
while(!bConnectionQuit &&
(clientReceiveLine(lpClient, szLine, sizeof(szLine)) >= 0))
{
// Treat them one at a time
switch(lpClient->iState)
{ // Command mode?
case STATE_WAIT_FOR_COMMAND:
{
int iLoop;
DEBUGIT("STATE_WAIT_FOR_COMMAND");
if(!(*szLine))
{
smtpSendMessage(lpClient, 500, MSG_UNKNOWN);
continue; // CR, LF, or '\0' came across
}
pLine=szLine;
while((*pLine!='\0')&&(*pLine!=' ')&&(*pLine!='\n'))
pLine++;
AnsiLower((LPSTR)szLine);
if(*pLine!='\0')
{
*(pLine++)='\0';
}
// szLine now holds the lowercase command
// pLine points to the remaining parameters
iLoop=0;
bCmdOk=FALSE;
while(smtpCodesTable[iLoop].SMTPCommand != NULL)
{
if(lstrcmp((LPSTR)smtpCodesTable[iLoop].SMTPCommand,
(LPSTR)szLine)==0)
{
smtpParser(lpClient, smtpCodesTable[iLoop].SMTPCode, pLine);
bCmdOk=TRUE;
break;
}
iLoop++;
}
if(!bCmdOk) smtpSendMessage(lpClient, 500, MSG_UNKNOWN);
}
break;
// Data mode?
case STATE_WAIT_FOR_DOT:
{
DEBUGIT("STATE_WAIT_FOR_DOT");
if((szLine[0]=='.')&&(szLine[1]=='\0'))
{
lpClient->iState=STATE_WAIT_FOR_COMMAND;
if(lpClient->hfFile!=HFILE_ERROR)
{
_lclose(lpClient->hfFile);
}
lpClient->hfFile=HFILE_ERROR;
// Handle MAIL/SAML/SOML as MAIL
if((lpClient->iMessageType==CMDMAIL)||
(lpClient->iMessageType==CMDSAML)||
(lpClient->iMessageType==CMDSOML))
{
smtpAppendToFile(lpClient, szLocalMailPath,
lpClient->szFile);
}
// Handle SAML/SEND as SEND also
if((lpClient->iMessageType==CMDSAML)||
(lpClient->iMessageType==CMDSEND))
{
smtpDisplayFile(lpClient, lpClient->szFile);
}
smtpSendMessage(lpClient, 250, MSG_OK);
continue;
}
else if((szLine[0]=='.')&&(szLine[1]=='.'))
{ // Byte stuff it! Someone tried to send a '.' in their
// message. Only the first '.' is doubled.
lstrcpy((LPSTR)szPaddedLine, (LPSTR)(szLine+1));
lstrcpy((LPSTR)szLine, (LPSTR)szPaddedLine);
}
// All non '.' lines are stored.
if(lpClient->hfFile!=HFILE_ERROR)
{
wsprintf((LPSTR)szPaddedLine, "%s\r\n", (LPSTR)szLine);
iDebug=_lwrite(lpClient->hfFile,
(LPSTR)szPaddedLine, lstrlen((LPSTR)szPaddedLine));
}
continue;
} /* case(data mode) */
break;
} /* switch(mode); */
if(bConnectionQuit) break;
} /* while(lines exist);*/
DEBUGIT("smtpServer() End");
return(FALSE);
} /* smtpServer() */
/* BOOL smtpParser(LPSMTPCLIENT, int, LPSTR);
Purpose: The actual parser of the SMTP "language"
Given: Client pointer, Token of command, and it's arguments
*/
BOOL smtpParser(LPSMTPCLIENT lpClient, int iToken, LPSTR lpArgs)
{
DEBUGIT("smtpParser() Begin");
switch(iToken)
{
// HELO - SMTP client is identifying itself
case CMDHELO:
{
DEBUGIT("CMDHELO Begin");
if(lstrcmpi(lpClient->szPeer, lpArgs) != 0)
{
if(lstrlen(lpArgs) == 0)
smtpSendMessage(lpClient, 250, MSG_WHOAREYOU_S_S,
(LPSTR)szLocalHostName,
lpClient->szPeer);
else
smtpSendMessage(lpClient, 250, MSG_WHOAREYOU_S_S_S,
(LPSTR)szLocalHostName,
lpClient->szPeer,
lpArgs);
}
else
{
smtpSendMessage(lpClient, 250, MSG_HELLO_S_S,
(LPSTR)szLocalHostName,
lpArgs);
}
DEBUGIT("CMDHELO End");
return(FALSE);
}
// EHLO - ESMTP client is identifying itself
case CMDEHLO:
{
DEBUGIT("CMDEHLO Begin");
if(lstrcmpi(lpClient->szPeer,
lpArgs) != 0)
{
if(lstrlen(lpArgs) == 0)
smtpSendMessage(lpClient, 250, MSG_HWOAREYOU_S_S,
(LPSTR)szLocalHostName,
lpClient->szPeer);
else
smtpSendMessage(lpClient, 250, MSG_HWOAREYOU_S_S_S,
(LPSTR)szLocalHostName,
lpClient->szPeer,
lpArgs);
}
else
{
smtpSendMessage(lpClient, 250, MSG_EHLLO_S_S,
(LPSTR)szLocalHostName,
lpArgs);
}
lpClient->iExtendedFlags |= ESMTP_EHLO_USED;
DEBUGIT("CMDEHLO End");
return(FALSE);
}
// Oh, joy.
case CMDSEND: // Send user a message to screen
case CMDSAML: // Send to screen AND store as mail
case CMDSOML: // Send to screen OR store as mail
case CMDMAIL: // Store as mail to user
{
LPSTR lpPtr;
LPSTR lpWalk;
LPSTR lpMark;
BOOL bSentReply = FALSE;
DEBUGIT("CMDMAIL Begin");
lpClient->iMessageType=iToken;
if(lpClient->bHaveFrom)
{
smtpSendMessage(lpClient, 503, MSG_ALREADYSENDER);
return(FALSE);
}
else
{
if(*lpArgs=='\0') break;
if( ( lpPtr = smtpSkipWord( lpClient, lpArgs, (LPSTR)"from" )) != NULL )
{
lpClient->bHaveFrom=TRUE;
// No long name crashes!
if(lstrlen(lpPtr)>=sizeof(lpClient->szFrom))
lpPtr[sizeof(lpClient->szFrom)-1] = '\0';
// 8BITMIME and other ESMTP hacks
if(lpClient->iExtendedFlags & ESMTP_EHLO_USED)
{
// Check for ESMTP commands after the Sender's address
lpWalk=lpPtr;
while((*lpWalk!='>') && *lpWalk ) lpWalk++;
if((*lpWalk)&&(*(lpWalk+1)))
{
if(*(++lpWalk)==' ')
{
while(*lpWalk==' ') *lpWalk++ = '\0';
lpMark=lpWalk;
while((*lpWalk!='=') && *lpWalk ) lpWalk++;
if(*lpWalk) *lpWalk++ = '\0';
if(lstrcmpi(lpMark, "BODY") == 0)
{
lpMark=lpWalk;
while((*lpWalk!=' ') && *lpWalk ) lpWalk++;
if(*lpWalk) *lpWalk++ = '\0';
if(lstrcmpi(lpMark, "8BITMIME") == 0)
{
lpClient->iExtendedFlags |= ESMTP_8BITMIME;
bSentReply=TRUE;
smtpSendMessage(lpClient, 250, MSG_SENDEROK_8BITMIME_S,
(LPSTR)lpPtr);
} // if 8BITMIME found
} // if BODY= found
} // if address is followed by a space
} // if something was found after the address
} // if extended commands are in effect
lstrcpy(lpClient->szFrom,
lpPtr);
if(!bSentReply) smtpSendMessage(lpClient, 250, MSG_SENDEROK_S,
lpPtr);
DEBUGIT("CMDMAIL End (Extended)");
return(FALSE);
}
// a 501 error was already sent
DEBUGIT("CMDMAIL End (Already Sender)");
return(FALSE);
}
DEBUGIT("CMDHELO End (Normal)");
} break;
// RCPT - Client is identifying recipient's ID
case CMDRCPT:
{
LPSTR lpPtr;
DEBUGIT("CMDRCPT Begin");
if( *lpArgs == '\0' ) break;
if( ( lpPtr = smtpSkipWord( lpClient, lpArgs, (LPSTR)"to" )) != NULL )
{
// No long name crashes!
if(lstrlen(lpPtr)>=sizeof(lpClient->szTo))
lpPtr[sizeof(lpClient->szTo)-1] = '\0';
lstrcpy(lpClient->szTo,
lpPtr);
smtpSendMessage( lpClient, 250, MSG_RECIPIENTOK_S,
lpPtr );
DEBUGIT("CMDRCPT End (Recipient ok)");
return(FALSE);
}
// A 501 error was already sent
DEBUGIT("CMDRCPT End (???)");
return(FALSE);
} break;
// DATA - Client is sending actual mail message
case CMDDATA:
{
HFILE hfFile;
char szFile[MAXFILENAME];
OFSTRUCT ofOpenFileStruct;
DEBUGIT("CMDDATA Begin");
if(!lpClient->bHaveFrom)
{
smtpSendMessage(lpClient, 503, MSG_NEEDMAIL);
DEBUGIT("CMDDATA End (Need mail?)");
return(FALSE);
}
// Open a temporary file
GetTempFileName(0, (LPSTR)STRING_PREFIX, 0, (LPSTR)szFile);
hfFile=OpenFile((LPSTR)szFile, (OFSTRUCT FAR *)&ofOpenFileStruct,
OF_CREATE | OF_WRITE | OF_SHARE_EXCLUSIVE);
if(hfFile==HFILE_ERROR)
{
smtpSendMessage(lpClient, 503, MSG_NOTEMP);
DEBUGIT("CMDDATA End (No temporary file)");
return(FALSE);
}
// Ok, everything is ready to start the message.
lpClient->iState=STATE_WAIT_FOR_DOT;
lpClient->hfFile=hfFile;
lstrcpy(lpClient->szFile,
(LPSTR)szFile);
smtpMakeHeader(lpClient, hfFile, lpClient->szPeer,
lpClient->szFrom,
lpClient->szTo);
if(lpClient->iExtendedFlags & ESMTP_8BITMIME)
smtpSendMessage(lpClient, 354, MSG_SEND8BITMIME);
else
smtpSendMessage(lpClient, 354, MSG_SENDDATA);
DEBUGIT("CMDDATA End");
return(FALSE);
}
case CMDRSET:
{
DEBUGIT("CMDRSET Begin");
lpClient->iState=STATE_WAIT_FOR_COMMAND;
lpClient->iExtendedFlags=ESMTP_NONE;
lpClient->bHaveFrom=FALSE;
lpClient->szFrom[0]='\0';
lpClient->szTo[0]='\0';
smtpSendMessage(lpClient, 250, MSG_RESET);
DEBUGIT("CMDRSET End");
return(FALSE);
}
case CMDVRFY:
{
DEBUGIT("CMDVRFY Begin");
smtpSendMessage(lpClient, 250, MSG_IDONTDOTHATYET);
DEBUGIT("CMDVRFY Begin");
return(FALSE);
}
case CMDHELP:
{
DEBUGIT("CMDHELP Begin");
if(*lpArgs=='\0')
{
smtpSendHelp(lpClient, NULL);
}
else smtpSendHelp(lpClient, lpArgs);
DEBUGIT("CMDHELP End");
return(FALSE);
} break;
case CMDNOOP:
{
DEBUGIT("CMDNOOP Begin");
smtpSendMessage(lpClient, 200, MSG_OK);
DEBUGIT("CMDNOOP Begin");
return(FALSE);
}
case CMDQUIT:
{
DEBUGIT("CMDQUIT Begin");
smtpSendMessage(lpClient, 221, MSG_GOODBYE_S,
(LPSTR)szLocalHostName);
smtpDestroyClient(lpClient);
bConnectionQuit=TRUE;
DEBUGIT("CMDQUIT End");
return(FALSE);
}
case CMDVERB:
{
DEBUGIT("CMDVERB Begin");
smtpSendMessage(lpClient, 200, MSG_VERBOSEMODE);
DEBUGIT("CMDVERB End");
return(FALSE);
}
case CMDONEX:
{
DEBUGIT("CMDONEX Begin");
smtpSendMessage(lpClient, 200, MSG_ONETRANSACTION);
DEBUGIT("CMDONEX End");
return(FALSE);
}
case CMDTICK:
{
DEBUGIT("CMDTICK Begin");
smtpSendMessage(lpClient, 250, MSG_OK);
DEBUGIT("CMDTICK End");
return(FALSE);
}
case CMDXWIN3:
{
DEBUGIT("CMDXWIN3 Begin");
smtpSendMessage(lpClient, 250, MSG_WIN3OK);
DEBUGIT("CMDXWIN3 End");
return(FALSE);
}
case CMDDBGQSHOW:
{
DEBUGIT("CMDDBGQSHOW Begin");
smtpSendMessage(lpClient, 200, MSG_SHOWQ);
DEBUGIT("CMDDBGQSHOW End");
return(FALSE);
}
case CMDDBGDEBUG:
{
DEBUGIT("CMDDBGDEBUG Begin");
smtpSendMessage(lpClient, 200, MSG_DEBUGSET);
DEBUGIT("CMDDBGDEBUG End");
return(FALSE);
}
case CMDMULT:
{
DEBUGIT("CMDMULT Begin");
smtpSendMessage(lpClient, 250, MSG_OK);
DEBUGIT("CMDMULT End");
return(FALSE);
}
case CMDERROR:
DEBUGIT("CMDERROR");
default:
break;
}
smtpSendMessage(lpClient, 500, MSG_UNKNOWN);
DEBUGIT("smtpParser() End");
return(FALSE);
} /* smtpParser() */
/* void smtpMakeHeader(LPSMTPCLIENT, HFILE, LPSTR, LPSTR, LPSTR);
Purpose: To create the "header" portion of the mail
file.
Given: Client pointer, Open (and valid) file handle,
Peer name, From and To strings.
Returns: Nothing.
*/
void smtpMakeHeader(LPSMTPCLIENT lpClient, HFILE hfFile, LPSTR lpPeer,
LPSTR lpFrom, LPSTR lpTo)
{
int iSize;
char szTime[30];
char szBuffer[MAXLINE];
LPSOCKADDR_IN lpsaHostAddr;
DEBUGIT("smtpMakeHeader() Begin");
lpsaHostAddr=(LPSOCKADDR_IN)&(lpClient->saPeer);
netGetTimeAndDate((LPSTR)szTime, sizeof(szTime));
iSize=wsprintf((LPSTR)szBuffer, "\r\nFrom %s %s\r\n",
lpFrom, (LPSTR)szTime);
_lwrite(hfFile, (LPSTR)szBuffer, iSize);
iSize=wsprintf((LPSTR)szBuffer, "X-Envelope-To: %s\r\n",
lpTo);
_lwrite(hfFile, szBuffer, iSize);
iSize=wsprintf((LPSTR)szBuffer, "Return-Path: %s\r\n",
lpFrom);
_lwrite(hfFile, (LPSTR)szBuffer, iSize);
if(lpClient->iExtendedFlags & ESMTP_8BITMIME)
{
iSize=wsprintf((LPSTR)szBuffer,
"Received: from %s [%s] by %s (1.0/WINSMTPSRV/8BITMIME)\r\n",
lpPeer,
inet_ntoa(lpsaHostAddr->sin_addr),
/* inet_ntoa() and [%s] or this and [%d.%d.%d.%d], take your pick
lpsaHostAddr->sin_addr.S_un.S_un_b.s_b1,
lpsaHostAddr->sin_addr.S_un.S_un_b.s_b2,
lpsaHostAddr->sin_addr.S_un.S_un_b.s_b3,
lpsaHostAddr->sin_addr.S_un.S_un_b.s_b4,
*/
(LPSTR)szLocalHostName);
}
else
{
iSize=wsprintf((LPSTR)szBuffer,
"Received: from %s [%s] by %s (1.61/WINSMTPSRV)\r\n",
lpPeer,
inet_ntoa(lpsaHostAddr->sin_addr),
(LPSTR)szLocalHostName);
}
_lwrite(hfFile, (LPSTR)szBuffer, iSize);
// Log incoming to/from
wsprintf((LPSTR)szBuffer, "Message from: %s to: %s",
lpFrom, lpTo);
SendDlgItemMessage(hWndDlg, IDL_LOG, LB_ADDSTRING, 0,
(LPARAM)(LPCSTR)szBuffer);
DEBUGIT("smtpMakeHeader() End");
return;
} /* smtpMakeHeader */
/* void smtpSendHelp(LPSMTPCLIENT, LPSTR);
Purpose: To give the remote user help (not batch, obviously)
Given: Client Index, Topic
Returns: Nothing.
*/
void smtpSendHelp(LPSMTPCLIENT lpClient,LPSTR lpTopic)
{
LPSTR lpLine;
int iLoop;
WORD wTopic;
char szBuffer[MAXSNDBUFF];
DEBUGIT("smtpSendHelp() Begin");
if(lpTopic!=NULL)
{
lpLine=lpTopic;
// pLine points to the HELP parameters
iLoop=0;
wTopic=CMDHELP;
while(smtpCodesTable[iLoop].SMTPCommand != NULL)
{
if(lstrcmp((LPSTR)smtpCodesTable[iLoop].SMTPCommand,
lpLine)==0)
{
wTopic=(WORD)smtpCodesTable[iLoop].SMTPCode;
break;
}
iLoop++;
}
}
else wTopic=CMDHELP;
smtpSendMessage(lpClient, 214, wTopic);
// Kludge
if(lpTopic==NULL)
{
lstrcpy((LPSTR)szBuffer, "HELP");
}
else
{
lstrcpy((LPSTR)szBuffer, (LPSTR)smtpCodesTable[iLoop].SMTPCommand);
AnsiUpper((LPSTR)szBuffer);
}
smtpSendMessage(lpClient, 214, MSG_END_HELP_S, (LPSTR)szBuffer);
DEBUGIT("smtpSendHelp() End");
return;
} /* smtpSendHelp */
/* LPSTR smtpSkipWord(LPSMTPCLIENT, LPSTR, LPSTR);
Purpose: To skip over words not neccisary on an SMTP line.
Given: Client pointer, string to examine, word to skip
Returns: NULL if error, rest of line if not
*/
LPSTR smtpSkipWord(LPSMTPCLIENT lpClient, LPSTR lpString, LPSTR lpWord)
{
LPSTR lpTemp;
DEBUGIT("smtpSkipWord() Begin");
while((*lpString==' ')||(*lpString=='\t')) lpString++;
lpTemp=lpString;
while((*lpString!='\0')&&(*lpString!=':')&&
!((*lpString==' ')||(*lpString=='\t'))) lpString++;
while((*lpString==' ')||(*lpString=='\t')) *(lpString++)=='\0';
if(*lpString!=':')
{
smtpSendMessage(lpClient, 501, MSG_SYNTAXERROR);
return(NULL);
}
*(lpString++)='\0';
while((*lpString==' ')||(*lpString=='\t')) lpString++;
if(lstrcmpi(lpTemp, lpWord))
{
smtpSendMessage(lpClient, 501, MSG_SYNTAXERROR);
return(NULL);
}
DEBUGIT("smtpSkipWord() End");
return(lpString);
} /* smtpSkipWord */
/* void smtpAppendToFile(LPSMTPCLIENT, LPSTR, LPSTR);
Purpose: To copy one file to the end of another.
Given: Pointer to a SMTPCLIENT for error messages,
Filename of the file to append to,
Filename of the file to append from.
Returns: Nothing.
*/
void smtpAppendToFile(LPSMTPCLIENT lpClient, LPSTR lpTo, LPSTR lpFrom)
{
HFILE hTo, hFrom;
char szBuffer[MAXSNDBUFF];
int iSize;
OFSTRUCT ofToFileStruct,
ofFromFileStruct;
DEBUGIT("smtpAppendToFile() Begin");
// Open the MAIL file for reading
hTo=OpenFile(lpTo, (OFSTRUCT FAR *)&ofToFileStruct,
OF_WRITE | OF_SHARE_EXCLUSIVE);
// Does the main MAIL file exist?
if(hTo==HFILE_ERROR)
{
// No? Create one.
hTo=OpenFile(lpTo, (OFSTRUCT FAR *)&ofToFileStruct,
OF_CREATE | OF_WRITE | OF_SHARE_EXCLUSIVE);
// Serious error!
if(hTo==HFILE_ERROR)
{
smtpSendMessage(lpClient, 503, MSG_MAILOPEN);
smtpError(IDS_COULDNTOPEN_S, lpTo);
DEBUGIT("smtpAppendToFile() End (File error 1)");
return;
}
}
// Open the temporary file
hFrom=OpenFile(lpFrom, (OFSTRUCT FAR *)&ofToFileStruct,
OF_READ | OF_SHARE_DENY_WRITE);
if(hFrom==HFILE_ERROR)
{
smtpSendMessage(lpClient, 503, MSG_TEMPOPEN);
smtpError(IDS_COULDNTOPEN_S, lpFrom);
_lclose(hTo);
DEBUGIT("smtpAppendToFile() End (File error 2)");
return;
}
// Seek to the end of the main MAIL file
if(_llseek(hTo, 0, 2)==HFILE_ERROR)
{
smtpSendMessage(lpClient, 503, MSG_SEEKERROR);
smtpError(IDS_FILEERROR_S, lpTo);
_lclose(hTo);
_lclose(hFrom);
DEBUGIT("smtpAppendToFile() End (File error 3)");
return;
}
// Copy the temporary file to the end of the MAIL file
while(iSize=_lread(hFrom, (LPSTR)szBuffer, sizeof(szBuffer)))
{
if((iSize==HFILE_ERROR)||
(_lwrite(hTo, (LPSTR)szBuffer, iSize)==HFILE_ERROR)) break;
}
// Close 'em both
_lclose(hTo);
_lclose(hFrom);
// Icon Flag!
bMailArrived=TRUE;
RedrawWindow(hWndDlg, NULL, NULL, RDW_INTERNALPAINT);
// Tell user mail just came
MessageBeep(MB_ICONEXCLAMATION);
// Wait a bit and check to see if this mail still exists
SetTimer(hWndDlg, (UINT)ID_TIMER, (UINT)TIME_CHECK, NULL);
// Delete the temporary file
if(!lpClient) return;
if(lpClient->iMessageType!=CMDSAML)
{
OpenFile(lpFrom, (OFSTRUCT FAR *)&ofFromFileStruct,
OF_DELETE);
}
DEBUGIT("smtpAppendToFile() End");
return;
} /* smtpAppendToFile() */
/* BOOL smtpMailCheck(LPSTR);
Purpose: To check to see if the main mail file still exists.
Given: Filename of the main file to check for.
Returns: Nothing.
Other: Modifies mail flag
*/
BOOL smtpMailCheck(LPSTR lpFilename)
{
OFSTRUCT ofFile;
HICON hIcon;
if(OpenFile(lpFilename, (OFSTRUCT FAR *)&ofFile,
OF_EXIST | OF_SHARE_COMPAT) != HFILE_ERROR)
{
bMailArrived=TRUE;
hIcon=LoadIcon(hInst, MAKEINTRESOURCE(IDI_ICONFULL));
}
else
{
bMailArrived=FALSE;
hIcon=LoadIcon(hInst, MAKEINTRESOURCE(IDI_ICONEMPTY));
}
RedrawWindow(hWndDlg, NULL, NULL, RDW_INTERNALPAINT);
SetProp(hWndDlg, "icon", hIcon);
return(bMailArrived);
} /* smtpMailCheck() */
/* BOOL smtpDisplayFile(LPSMTPCLIENT, LPSTR);
Purpose: To display a mail message to the screen for
SEND and SAML.
Given: Pointer to a related SMTPCLIENT struct, filename
Return: TRUE if error, FALSE if not.
*/
BOOL smtpDisplayFile(LPSMTPCLIENT lpClient, LPSTR lpFilename)
{
char szCommand[256];
OFSTRUCT ofFile;
DEBUGIT("smtpDisplayFile() Begin");
// Beep user to tell them mail came.
MessageBeep(MB_ICONEXCLAMATION);
// "Fork" off a notepad to handle it
wsprintf((LPSTR)szCommand, "notepad %s", lpFilename);
if(WinExec((LPSTR)szCommand, SW_SHOW)<32)
{
smtpAppendToFile(lpClient, (LPSTR)szLocalMailPath, lpFilename);
}
// Delete the temporary file
if((lpClient->iMessageType==CMDSAML)||
(lpClient->iMessageType==CMDSEND))
{
OpenFile(lpFilename, (OFSTRUCT FAR *)&ofFile,
OF_DELETE);
}
DEBUGIT("smtpDisplayFile() End");
return(FALSE);
} /* smtpDisplayFile() */
/* LPSMTPCLIENT smtpAddClient(SOCKET);
Purpose: Allocate a client structure and initilize it.
Given: SOCKET of a connection.
Returns: A negative number on error, or a
valid SMTPCLIENT index into
smtpClientsTable
Reusability notes:
SMTPCLIENT smtpClientsTable;
PHASE_INIT,3 MAXBUFF, MAXCLIENTS;
*/
LPSMTPCLIENT smtpAddClient(SOCKET sSocket)
{
int iRcvSize;
int iSizeofRcvSize;
HANDLE hInput, hOutput;
HCLIENT hClient;
LPSTR lpInput, lpOutput;
LPSMTPCLIENT lpClient, lpWalker;
DEBUGIT("smtpAddClient() Begin");
// Find the receiver buffer size
iSizeofRcvSize=sizeof(iRcvSize);
getsockopt(sSocket, SOL_SOCKET, SO_RCVBUF, (LPSTR)&iRcvSize,
(LPINT)&iSizeofRcvSize );
// Make sure we can handle a certain size line at a time
if(iRcvSize<MAXSNDBUFF) iRcvSize=MAXSNDBUFF;
// Make sure we can handle up to 2 buffers at a time
iRcvSize=iRcvSize*2;
// Allocate the receiver buffer
hInput=GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT,
iRcvSize);
if(!hInput) return(NULL);
lpInput=GlobalLock(hInput);
if(!lpInput)
{
GlobalFree(hInput);
return(NULL);
}
// Allocate the sending buffer
// The size is permanent, we only need dynamic with incoming
hOutput=GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT,
MAXSNDBUFF);
if(!hOutput)
{
GlobalUnlock(hInput);
GlobalFree(hInput);
return(NULL);
}
lpOutput=GlobalLock(hOutput);
if(!lpOutput)
{
GlobalFree(hOutput);
GlobalUnlock(hInput);
GlobalFree(hInput);
return(NULL);
}
// Allocate a SMTPCLIENT element
hClient=GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT,
sizeof(SMTPCLIENT));
if(!hClient)
{
GlobalUnlock(hOutput);
GlobalFree(hOutput);
GlobalUnlock(hInput);
GlobalFree(hInput);
return(NULL);
}
lpClient=(LPSMTPCLIENT)GlobalLock(hClient);
if(!lpClient)
{
GlobalFree(hClient);
GlobalUnlock(hOutput);
GlobalFree(hOutput);
GlobalUnlock(hInput);
GlobalFree(hInput);
return(NULL);
}
lpClient->sSocket=sSocket;
lpClient->bHaveFrom=FALSE;
lpClient->hfFile=HFILE_ERROR;
lpClient->hClient=hClient;
lpClient->lpOutputBuffer=lpOutput;
lpClient->hOutputBuffer=hOutput;
// iOutputSize is MAXSNDBUFF
lpClient->lpInputBuffer=lpInput;
lpClient->hInputBuffer=hInput;
lpClient->iInputSize=iRcvSize;
lpClient->fifoOutputStart=0;
lpClient->fifoOutputStop=0;
lpClient->fifoInputStart=0;
lpClient->fifoInputStop=0;
lpClient->iState=STATE_WAIT_FOR_COMMAND;
lpClient->iExtendedFlags=ESMTP_NONE;
lpClient->lpNextClient=NULL;
lpClient->lpPrevClient=NULL;
// If this is the main listening socket, initialize List
if(!smtpClientsHead)
{
smtpClientsHead=lpClient;
}
else
{
lpWalker=smtpClientsHead;
while(lpWalker->lpNextClient) lpWalker=lpWalker->lpNextClient;
lpWalker->lpNextClient=lpClient; // Add new tail segment
lpClient->lpPrevClient=lpWalker; // Tell tail who's its parent
}
DEBUGIT("smtpAddClient() End");
return(lpClient);
} /* smtpAddClient */
/* LPSMTPCLIENT smtpSocketToClient(SOCKET sSocket) */
LPSMTPCLIENT smtpSocketToClient(SOCKET sSocket)
{
LPSMTPCLIENT lpClient;
if((!lpClient)||(sSocket==SOCKET_ERROR)) return(NULL);
lpClient=smtpClientsHead;
while(lpClient!=NULL)
{
if(lpClient->sSocket==sSocket) return(lpClient);
lpClient=lpClient->lpNextClient;
}
return(lpClient);
} /* smtpSocketToClient() */
/* BOOL smtpDestroyClient(LPSMTPCLIENT);
Purpose: To release a SMTPCLIENT structure.
Given: Linked list element pointer
Returns: TRUE if error, FALSE if all OK
Reusability notes:
*/
BOOL smtpDestroyClient(LPSMTPCLIENT lpClient)
{
BOOL bLinger = FALSE;
LPSMTPCLIENT lpNext, lpPrev;
DEBUGIT("smtpDestroyClient() Begin");
if(!lpClient) return(TRUE);
// This code was removed as "dead" code. The setsockopt() just sets
// the SO_LINGER option to what it already should be by default
// (thanks Bob (rcq@ftp.com))
// setsockopt(lpClient->sSocket,
// SOL_SOCKET, SO_LINGER, (char FAR *)&bLinger,
// sizeof(BOOL));
// This shutdown isn't needed either...
// shutdown(lpClient->sSocket, 2);
closesocket(lpClient->sSocket);
smtpLog(LOG_HIGH | LOG_TIME, LOG_DISCONNECT_S, (LPSTR)lpClient->szPeer);
// Hack Alert! This is a hack, this is only a hack. If this were
// real winsock compliant code, it shouldn't have to do this!
// But it doesn't hurt, and Lanera seems to need it?
// Since this is non-standard, it is now no longer needed - but I
// doubt if it will work on Lanera's stack anymore..
//if(lpClient!=smtpClientsHead)
//{
// WSAAsyncSelect(smtpClientsHead->sSocket, hWndMain, NET_ACTIVITY,
// FD_ACCEPT | FD_READ | FD_WRITE | FD_CLOSE);
//}
// Is the DATA temporary file still open?
if(lpClient->hfFile!=HFILE_ERROR)
{
_lclose(lpClient->hfFile);
// Take it as valid - but don't send any messages
smtpAppendToFile(NULL, szLocalMailPath,
lpClient->szFile);
}
// Patch out THIS segment.
lpNext=lpClient->lpNextClient;
lpPrev=lpClient->lpPrevClient;
if(lpPrev) lpPrev->lpNextClient=lpNext;
// Free all of the memory
GlobalUnlock(lpClient->hInputBuffer);
GlobalFree(lpClient->hInputBuffer);
GlobalUnlock(lpClient->hOutputBuffer);
GlobalFree(lpClient->hOutputBuffer);
GlobalUnlock(lpClient->hClient);
GlobalFree(lpClient->hClient);
DEBUGIT("smtpDestroyClient() End");
return(FALSE);
} /* smtpDestroyClient */
/* void smtpSendMessage(LPSMTPCLIENT, int, int, ...);
Purpose: To send a resource/reply message.
Given: index into smtpClientsTable
SMTP reply number (211, 503)
Resource ID of the reply string.
... arguments
Returns: Nothing.
Other: If the Resource ID String has a '\n' in it,
this routine will send it line at a time.
*/
void smtpSendMessage(LPSMTPCLIENT lpClient, int SMTPCode, int ResourceID, ...)
{
int iCount;
char szString[MAXSNDBUFF];
char szLine[MAXSNDBUFF];
LPSTR lpArg1, lpArg2, lpArg3;
PSTR pStart, pStop;
va_list vaArgs;
DEBUGIT("smtpSendMessage() Begin");
if(!lpClient) return;
va_start( vaArgs, ResourceID);
if(!LoadString(hInst, ResourceID, (LPSTR)szLine, sizeof(szLine)))
return;
lpArg1=va_arg(vaArgs, LPSTR);
lpArg2=va_arg(vaArgs, LPSTR);
lpArg3=va_arg(vaArgs, LPSTR);
wsprintf((LPSTR)szString, (LPSTR)szLine, lpArg1, lpArg2, lpArg3);
pStart=pStop=szString;
do /* Line loop for embedded '\n's */
{
while((*pStop!='\0')&&(*pStop!='\n')) pStop++;
if((*pStop!='\0'))
{
*(pStop++)='\0';
wsprintf((LPSTR)szLine, "%d-%s", SMTPCode, (LPSTR)pStart);
pStart=pStop;
}
else
{
wsprintf((LPSTR)szLine, "%d %s", SMTPCode, (LPSTR)pStart);
}
clientSendLine(lpClient, (LPSTR)szLine, lstrlen((LPSTR)szLine));
DEBUGIT((LPSTR)szLine);
smtpLog(LOG_HIGH, LOG_SENT_S, (LPCSTR)szLine);
} while(*pStop!='\0');
DEBUGIT("smtpSendMessage() End");
} /* smtpSendMessage */
/* int clientSendLine(LPSMTPCLIENT, LPSTR, int);
Purpose: Send 1 line to the caller if it exists.
Given: SMTPCLIENT index, Line pointer,
Line size. (for binary "string" sends if needed)
Returns: 0 if everything isn't ok.
>0 tells the caller how many characters it sent.
Reusability requirements:
Globals: SMTPCLIENT smtpClientsTable[]
Defines: MAXCLIENTS, MAXBUFF
Other: THIS ROUTINE ADDS A CR/LF PAIR!
*/
int clientSendLine(LPSMTPCLIENT lpClient, LPSTR lpLine, int iLine)
{
LPSTR lpBuffer;
int fifoStart;
int fifoStop;
int fifoWalker;
int iDest;
/* Catch illegal arguements */
if((!lpClient)||(lpLine==NULL)||(iLine==0))
return(0);
/* Make things easier to deal with */
lpBuffer=lpClient->lpOutputBuffer;
fifoStart=lpClient->fifoOutputStart;
fifoWalker=fifoStart;
fifoStop=lpClient->fifoOutputStop;
/* Make sure there is enough room - Egad */
if(( (fifoStop<fifoStart) &&
(((fifoStop+iLine+2)&MAXSNDBUFF)>=fifoStart)
) ||
( (fifoStop>=fifoStart) &&
(((fifoStop+iLine+2)/MAXSNDBUFF)==1) &&
(((fifoStop+iLine+2)%MAXSNDBUFF)>=fifoStart)
))
return(0);
/* There is room at the end! Copy it in! */
iDest=0;
fifoWalker=fifoStop;
while(iDest<iLine)
{
lpBuffer[fifoWalker]=lpLine[iDest];
fifoWalker=(fifoWalker+1)%MAXSNDBUFF;
iDest++;
}
lpBuffer[fifoWalker]='\r'; // Append CR on output
fifoWalker=(fifoWalker+1)%MAXSNDBUFF;
lpBuffer[fifoWalker]='\n'; // Append LF on output
fifoWalker=(fifoWalker+1)%MAXSNDBUFF;
lpClient->fifoOutputStop=fifoWalker;
// This is important
netSendData(lpClient);
return(iDest); // Tell em how many we copied!
} /* clientSendLine */
/* int clientReceiveLine(LPSMTPCLIENT, LPSTR, int);
Purpose: Receive 1 line for the caller if it exists.
Given: SMTPCLIENT pointer, Line buffer pointer,
Line buffer size.
Returns: -1 if no string found in buffer
0 if just CR or NL
>0 tells the caller how many characters it was.
Reusability requirements:
None!
Notes: This routines takes into account backspaces,
deletes, multiple CRs and LFs.
*/
int clientReceiveLine(LPSMTPCLIENT lpClient, LPSTR lpLine, int iLine)
{
LPSTR lpBuffer;
int iCount;
int fifoStart;
int fifoStop;
int fifoWalker;
int iDest;
int iMax;
if((!lpClient)||(lpLine==NULL)||(iLine==0))
return(-1);
/* Make things easier to deal with */
lpBuffer=lpClient->lpInputBuffer;
fifoStart=lpClient->fifoInputStart;
fifoWalker=fifoStart;
fifoStop=lpClient->fifoInputStop;
iMax=lpClient->iInputSize;
if(fifoStart==fifoStop) return(-1);
/* Search buffer for a LF or CR BEFORE the end of the buffer! */
while((lpBuffer[fifoWalker]!='\n')&&
(lpBuffer[fifoWalker]!='\r'))
{
fifoWalker=(fifoWalker+1)%iMax;
if(fifoWalker==fifoStop)
return(-1);
}
/* We found the end of a string! Copy it out. */
*lpLine='\0';
iDest=0;
fifoWalker=fifoStart;
while((lpBuffer[fifoWalker]!='\n')&&
(lpBuffer[fifoWalker]!='\r'))
{
if((lpBuffer[fifoWalker]=='\b')|| // Backspace (^H)
(lpBuffer[fifoWalker]=='\177')) // Delete (^?)
{ // Interactive User is talking to us (and making mistakes)
if((--iDest)<0) iDest=0; // Backup if we can
fifoWalker=(fifoWalker+1)%iMax; // Step over ^H or ^?
continue; // for ^H^H... case
}
lpLine[iDest]=lpBuffer[fifoWalker];
fifoWalker=(fifoWalker+1)%iMax;
iDest++;
if(iDest>=iLine)
return(-1); // Not enough string space!!!
}
lpLine[iDest]='\0';
// Step past the '\r'
if((fifoWalker!=fifoStop)&&
(lpBuffer[fifoWalker]=='\r')) fifoWalker=(fifoWalker+1)%iMax;
// Step past the '\n'
if((fifoWalker!=fifoStop)&&
(lpBuffer[fifoWalker]=='\n')) fifoWalker=(fifoWalker+1)%iMax;
lpClient->fifoInputStart=fifoWalker;
smtpLog(LOG_HIGH, LOG_RECEIVED_S, (LPCSTR)lpLine);
return(iDest); // Tell em how many we copied!
} /* clientReceiveLine */
/* int clientReceiveBlock(int, LPSTR, int);
Purpose: Receive entire FIFO block for the caller if it exists.
Given: SMTPCLIENT index, Block buffer pointer,
Block buffer size.
Returns: 0 if everything isn't ok. (no characters waiting)
>0 tells the caller how many characters it was.
Reusability requirements:
Globals: SMTPCLIENT smtpClientsTable[]
Defines: MAXCLIENTS, MAXBUFF
Notes: This kind of defeats the idea of FIFO, but it does
allow for 8 bit binary transfers.
Since it was easier to implement 8BITMIME termination
in here, I have left it so. This is not good design.
*/
int clientReceiveBlock(LPSMTPCLIENT lpClient, LPSTR lpLine, int iLine)
{
LPSTR lpBuffer;
int fifoStart;
int fifoStop;
int fifoWalker;
int iDest;
int iMax;
if((!lpClient)||(lpLine==NULL)||(iLine==0))
return(0);
// Make things easier to deal with
lpBuffer=lpClient->lpInputBuffer;
fifoStart=lpClient->fifoInputStart;
fifoStop=lpClient->fifoInputStop;
iMax=lpClient->iInputSize;
if(fifoStart==fifoStop) return(0);
iDest=0;
fifoWalker=fifoStart;
while(fifoWalker!=fifoStop)
{
if(lpBuffer[fifoWalker]=='.')
{
// Check for presence of, and validity of "\r\n.\r\n"
// Elegant, yet ugly
// Possible bug: What if the above sequence comes split?
if(((((fifoWalker-2)%iMax) != fifoStart) &&
(lpBuffer[(fifoWalker-2)%iMax]=='\r'))
&&((((fifoWalker-1)%iMax) != fifoStart) &&
(lpBuffer[(fifoWalker-1)%iMax]=='\n'))
&&((((fifoWalker+1)%iMax) != fifoStop) &&
(lpBuffer[(fifoWalker+1)%iMax]=='\r'))
&&((((fifoWalker+2)%iMax) != fifoStop) &&
(lpBuffer[(fifoWalker+2)%iMax]=='\n'))
)
{ // End of message
if(lpClient->hfFile!=HFILE_ERROR)
{
_lclose(lpClient->hfFile);
}
lpClient->hfFile=HFILE_ERROR;
smtpAppendToFile(lpClient, szLocalMailPath,
lpClient->szFile);
lpClient->iState=STATE_WAIT_FOR_COMMAND;
smtpSendMessage(lpClient, 250, MSG_OK);
fifoWalker=(fifoWalker+2)%iMax;
break;
}
} // Pass EVERYTHING else through.
lpLine[iDest]=lpBuffer[fifoWalker];
fifoWalker=(fifoWalker+1)%iMax;
iDest++;
}
lpLine[iDest]='\0'; // Zero terminate it, for luck
lpClient->fifoInputStart=fifoWalker;
smtpLog(LOG_HIGH, LOG_RECEIVED_S, (LPCSTR)lpLine);
return(iDest); // Tell em how many we copied!
} /* clientReceiveBlock */
/**** End of WSMTPSRV.C ****/